#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Nuitka 模块打包工具
将 Python 模块打包成 PYD/SO 文件，支持动态加载和替换

作者: AI Assistant
日期: 2024

功能特点:
1. 将模块打包成 PYD/SO 文件
2. 支持自定义模块和第三方模块
3. 自动处理依赖关系
4. 支持动态加载和替换
5. 详细的日志记录
"""

import os
import sys
import shutil
import subprocess
import logging
import json
import importlib
import tempfile
from pathlib import Path
from typing import List, Dict, Optional, Union, Set
import ast
import pkgutil

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('./log/nuitka_module_build.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)


class ModuleDependencyAnalyzer:
    """
    模块依赖分析器
    分析 Python 模块的依赖关系
    """
    
    def __init__(self):
        self.analyzed_modules: Set[str] = set()
        self.dependencies: Set[str] = set()
    
    def analyze_file(self, file_path: Path) -> Set[str]:
        """
        分析 Python 文件的导入依赖
        
        Args:
            file_path: Python 文件路径
            
        Returns:
            Set[str]: 依赖模块集合
        """
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            
            tree = ast.parse(content)
            imports = set()
            
            for node in ast.walk(tree):
                if isinstance(node, ast.Import):
                    for alias in node.names:
                        imports.add(alias.name.split('.')[0])
                elif isinstance(node, ast.ImportFrom):
                    if node.module:
                        imports.add(node.module.split('.')[0])
            
            return imports
        except Exception as e:
            logger.warning(f"分析文件失败 {file_path}: {e}")
            return set()
    
    def analyze_module(self, module_name: str) -> Set[str]:
        """
        递归分析模块及其依赖
        
        Args:
            module_name: 模块名称
            
        Returns:
            Set[str]: 所有依赖模块
        """
        if module_name in self.analyzed_modules:
            return set()
        
        self.analyzed_modules.add(module_name)
        
        try:
            module = importlib.import_module(module_name)
            module_file = getattr(module, '__file__', None)
            
            if not module_file:
                return set()
            
            module_path = Path(module_file)
            
            # 分析当前模块
            if module_path.suffix == '.py':
                imports = self.analyze_file(module_path)
            else:
                imports = set()
            
            # 递归分析子模块
            if module_path.is_dir() or str(module_path).endswith('__init__.py'):
                package_path = module_path.parent if module_path.name == '__init__.py' else module_path
                
                for importer, modname, ispkg in pkgutil.iter_modules([str(package_path)]):
                    full_name = f"{module_name}.{modname}"
                    imports.update(self.analyze_module(full_name))
            
            # 过滤掉标准库模块
            stdlib_modules = set(sys.builtin_module_names)
            try:
                import distutils.sysconfig
                stdlib_path = distutils.sysconfig.get_python_lib(standard_lib=True)
                for item in Path(stdlib_path).iterdir():
                    if item.is_dir() and not item.name.startswith('.'):
                        stdlib_modules.add(item.name)
            except:
                pass
            
            filtered_imports = {imp for imp in imports if imp not in stdlib_modules}
            self.dependencies.update(filtered_imports)
            
            return filtered_imports
            
        except ImportError as e:
            logger.warning(f"导入模块失败 {module_name}: {e}")
            return set()
        except Exception as e:
            logger.error(f"分析模块失败 {module_name}: {e}")
            return set()


class NuitkaModuleBuilder:
    """
    Nuitka 模块打包器
    将 Python 模块打包成 PYD/SO 文件
    """
    
    def __init__(self, output_dir: str = "./built_modules"):
        """
        初始化模块打包器
        
        Args:
            output_dir: 输出目录
        """
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(parents=True, exist_ok=True)
        
        self.temp_dir = None
        self.module_configs = {}
        self.dependency_analyzer = ModuleDependencyAnalyzer()
        
        logger.info(f"初始化模块打包器，输出目录: {self.output_dir}")

    def _detect_compiler_flag(self) -> str:
        try:
            import shutil as _sh
            if _sh.which("cl.exe"):
                return "--msvc=latest"
            if _sh.which("clang-cl"):
                return "--clang"
        except Exception:
            pass
        return "--mingw64"
    
    def create_module_package(self, module_name: str, module_files: List[str], 
                            main_file: str = None) -> Path:
        """
        创建模块打包用的临时包
        
        Args:
            module_name: 模块名称
            module_files: 模块文件列表
            main_file: 主文件（可选）
            
        Returns:
            Path: 临时包路径
        """
        # 创建临时目录
        temp_dir = tempfile.mkdtemp(prefix=f"nuitka_module_{module_name}_")
        self.temp_dir = Path(temp_dir)
        
        # 创建包目录
        package_dir = self.temp_dir / module_name
        package_dir.mkdir(exist_ok=True)
        
        # 创建 __init__.py
        init_file = package_dir / "__init__.py"
        
        # 复制模块文件
        for file_path in module_files:
            src_file = Path(file_path)
            if not src_file.exists():
                logger.warning(f"文件不存在: {file_path}")
                continue
            
            # 如果是 Python 文件，直接复制
            if src_file.suffix == '.py':
                dst_file = package_dir / src_file.name
                shutil.copy2(src_file, dst_file)
                logger.info(f"复制文件: {src_file} -> {dst_file}")
            
            # 如果是目录，递归复制
            elif src_file.is_dir():
                dst_dir = package_dir / src_file.name
                shutil.copytree(src_file, dst_dir, dirs_exist_ok=True)
                logger.info(f"复制目录: {src_file} -> {dst_dir}")
        
        # 创建 __init__.py 内容
        init_content = self._generate_init_content(module_name, module_files, main_file)
        init_file.write_text(init_content, encoding='utf-8')
        logger.info(f"创建 __init__.py: {init_file}")
        
        return self.temp_dir
    
    def _generate_init_content(self, module_name: str, module_files: List[str], 
                              main_file: str = None) -> str:
        """
        生成 __init__.py 内容
        
        Args:
            module_name: 模块名称
            module_files: 模块文件列表
            main_file: 主文件
            
        Returns:
            str: __init__.py 内容
        """
        content = f"""# -*- coding: utf-8 -*-
\"\"\"
{module_name} 模块 - 由 Nuitka 打包
自动生成，请勿手动修改
\"\"\"

import sys
import os
from pathlib import Path

# 获取当前模块路径
current_dir = Path(__file__).parent

# 动态加载所有子模块
"""
        
        # 添加导入语句
        for file_path in module_files:
            file_name = Path(file_path).stem
            if file_name != '__init__' and file_name != module_name:
                content += f"from . import {file_name}\n"
        
        # 如果有主文件，添加主功能
        if main_file:
            main_name = Path(main_file).stem
            content += f"""
# 主功能
from .{main_name} import *

# 版本信息
__version__ = "1.0.0"
__author__ = "Nuitka Builder"
"""
        
        return content
    
    def build_module(self, module_name: str, source_path: Union[str, Path], 
                    include_dependencies: bool = True, 
                    optimization_level: int = 2) -> Optional[Path]:
        """
        构建单个模块
        
        Args:
            module_name: 模块名称
            source_path: 源路径（文件或目录）
            include_dependencies: 是否包含依赖
            optimization_level: 优化级别 (0-3)
            
        Returns:
            Optional[Path]: 构建结果路径
        """
        logger.info(f"开始构建模块: {module_name}")
        
        source_path = Path(source_path)
        
        if not source_path.exists():
            logger.error(f"源路径不存在: {source_path}")
            return None
        
        # 分析依赖
        if include_dependencies:
            logger.info("分析模块依赖...")
            if source_path.is_file() and source_path.suffix == '.py':
                imports = self.dependency_analyzer.analyze_file(source_path)
            else:
                imports = self.dependency_analyzer.analyze_module(module_name)
            
            logger.info(f"发现依赖: {imports}")
        
        # 准备构建参数
        build_params = [
            "--module",  # 构建为模块而不是独立程序
            f"--output-dir={self.output_dir}",
            f"--jobs={os.cpu_count() or 4}",
            "--clang",  # 使用 Clang 编译器
            "--lto=no",  # 关闭 LTO
            "--disable-ccache",
            f"--python-flag=-O{optimization_level}",
            "--quiet"  # 减少输出
        ]
        
        # 根据源类型处理
        if source_path.is_file():
            module_files = [source_path]
            temp_package = self.create_module_package(module_name, module_files)
            target_file = temp_package / module_name / "__init__.py"
        else:
            init_py = Path(source_path) / "__init__.py"
            if init_py.exists():
                target_file = init_py
            else:
                temp_package = self.create_module_package(module_name, [str(source_path)])
                target_file = temp_package / module_name / "__init__.py"
        
        # 构建命令
        command = [sys.executable, "-m", "nuitka"] + build_params + [str(target_file)]
        
        try:
            logger.info(f"执行构建命令: {' '.join(command)}")
            result = subprocess.run(command, capture_output=True, text=True)
            
            if result.returncode == 0:
                logger.info("模块构建成功")
                
                # 查找构建结果
                built_files = list(self.output_dir.glob(f"{module_name}.*"))
                if built_files:
                    result_file = built_files[0]
                    logger.info(f"构建结果: {result_file}")
                    return result_file
                else:
                    logger.error("未找到构建结果文件")
                    return None
            else:
                logger.error(f"构建失败，返回码: {result.returncode}")
                if result.stderr:
                    logger.error(f"错误信息: {result.stderr}")
                return None
                
        except Exception as e:
            logger.error(f"构建过程出错: {e}")
            return None
        finally:
            # 清理临时文件
            if self.temp_dir and self.temp_dir.exists():
                shutil.rmtree(self.temp_dir)
                logger.info(f"清理临时目录: {self.temp_dir}")

    def copy_third_party_package(self, package_name: str, dest_dir: Union[str, Path]) -> Optional[Path]:
        try:
            pkg = importlib.import_module(package_name)
            pkg_dir = Path(getattr(pkg, '__file__')).parent
            dest_dir = Path(dest_dir) / package_name
            if dest_dir.exists():
                shutil.rmtree(dest_dir)
            shutil.copytree(pkg_dir, dest_dir)
            return dest_dir
        except Exception as e:
            logger.error(f"复制第三方包失败 {package_name}: {e}")
            return None

    def prepare_vispy_module(self, dest_root: Union[str, Path]) -> Optional[Path]:
        dest_root = Path(dest_root)
        dest_root.mkdir(parents=True, exist_ok=True)
        return self.copy_third_party_package("vispy", dest_root)

    def build_vispy_pyd_to_module(self, module_output_dir: Union[str, Path] = "module") -> Optional[Path]:
        """使用Nuitka将vispy打包为单文件pyd并输出到module目录。

        - 目标文件: module/vispy.pyd
        - 同时包含必要的数据目录：vispy/resources 与 vispy/gloo/glsl
        - 包含关键子包：app、scene、visuals、gloo 以及 app.backends
        - 排除 PyOpenGL 可选依赖的跟随，避免硬绑定
        """
        try:
            import vispy
            v_dir = Path(getattr(vispy, "__file__")).parent
        except Exception:
            # 如果当前环境未安装vispy，则临时安装到临时目录
            try:
                tmp = Path(tempfile.mkdtemp(prefix="vispy_src_"))
                logger.info(f"环境未安装vispy，使用临时目录安装: {tmp}")
                cmd = [sys.executable, "-m", "pip", "install", "--no-deps", "--target", str(tmp), "vispy"]
                r = subprocess.run(cmd, capture_output=True, text=True)
                if r.returncode != 0:
                    logger.error(f"临时安装vispy失败: {r.stderr}")
                    shutil.rmtree(tmp, ignore_errors=True)
                    return None
                v_dir = tmp / "vispy"
                if not v_dir.exists():
                    logger.error(f"临时安装目录未找到vispy包: {v_dir}")
                    shutil.rmtree(tmp, ignore_errors=True)
                    return None
                # 记录用于后续清理
                self.temp_dir = tmp
            except Exception as e:
                logger.error(f"准备vispy源码失败: {e}")
                return None

        out_dir = self.output_dir
        target_pkg_dir = v_dir
        if not target_pkg_dir.exists():
            logger.error(f"未找到vispy 包目录: {target_pkg_dir}")
            return None

        build_params = [
            "--module",
            f"--output-dir={out_dir}",
            f"--jobs={os.cpu_count() or 4}",
            "--mingw64",
            "--lto=no",
            "--disable-ccache",
            "--show-progress",
            "--include-package=vispy.app",
            "--include-package=vispy.app.backends",
            "--include-package=vispy.scene",
            "--include-package=vispy.visuals",
            "--include-package=vispy.visuals.text",
            "--include-package=vispy.gloo",
            "--include-package=vispy.util",
            "--include-package=vispy.io",
            "--include-package=vispy.geometry",
            "--include-package=vispy.ext",
            "--include-package=vispy.color",
            "--include-package=vispy.glsl",
            "--nofollow-import-to=OpenGL",
            "--noinclude-custom-mode=OpenGL:nofollow",
            "--nofollow-import-to=OpenGL.platform",
            "--noinclude-custom-mode=OpenGL.platform:nofollow",
            "--nofollow-import-to=OpenGL.platform.win32",
            "--noinclude-custom-mode=OpenGL.platform.win32:nofollow",
            "--nofollow-import-to=OpenGL.GL",
            "--noinclude-custom-mode=OpenGL.GL:nofollow",
        ]

        res_args = []
        try:
            res_dir = v_dir / "resources"
            if res_dir.exists():
                res_args.append(f"--include-data-dir={str(res_dir)}=vispy/resources")
            glsl_dir = v_dir / "gloo" / "glsl"
            if glsl_dir.exists():
                res_args.append(f"--include-data-dir={str(glsl_dir)}=vispy/gloo/glsl")
        except Exception:
            pass

        command = [sys.executable, "-m", "nuitka"] + build_params + res_args + [str(target_pkg_dir)]

        try:
            logger.info(f"执行vispy模块构建命令: {' '.join(command)}")
            env = os.environ.copy()
            env.setdefault("VISPY_BACKEND", "pyside2")
            env.setdefault("CLCACHE_DISABLE", "1")
            env.setdefault("NUITKA_DISABLE_CCACHE", "1")
            env.setdefault("NUITKA_SCONS_TEMP", str(Path("C:/_mt_scons").absolute()))
            result = subprocess.run(command, capture_output=True, text=True, env=env)
            if result.returncode != 0:
                logger.error(f"vispy模块构建失败，返回码: {result.returncode}\n{result.stderr}")
                return None
            built_files = list(out_dir.glob("vispy.*"))
            pyd = next((p for p in built_files if p.suffix.lower() == ".pyd"), None)
            if not pyd:
                logger.error("未找到vispy生成的pyd文件")
                return None
            module_output_dir = Path(module_output_dir)
            try:
                old_pkg_dir = module_output_dir / "vispy"
                if old_pkg_dir.exists():
                    shutil.rmtree(old_pkg_dir)
            except Exception:
                pass
            module_output_dir.mkdir(parents=True, exist_ok=True)
            dest_pyd = module_output_dir / "vispy.pyd"
            shutil.copy2(pyd, dest_pyd)
            for r in (v_dir / "resources", v_dir / "gloo" / "glsl", v_dir / "glsl"):
                if r.exists():
                    if r.name == "resources":
                        rel = "vispy/resources"
                    elif r.name == "glsl":
                        rel = "vispy/glsl"
                    else:
                        rel = "vispy/gloo/glsl"
                    dest = module_output_dir / rel
                    dest.parent.mkdir(parents=True, exist_ok=True)
                    if dest.exists():
                        shutil.rmtree(dest)
                    shutil.copytree(r, dest)
            logger.info(f"vispy.pyd 已输出到: {dest_pyd}")
            # 额外复制 text 可视化子包，避免嵌套子模块运行时导入失败
            try:
                text_pkg = v_dir / "visuals" / "text"
                if text_pkg.exists():
                    dest_text = module_output_dir / "vispy" / "visuals" / "text"
                    if dest_text.exists():
                        shutil.rmtree(dest_text)
                    shutil.copytree(text_pkg, dest_text)
            except Exception:
                pass
            return dest_pyd
        except Exception as e:
            logger.error(f"构建vispy pyd过程出错: {e}")
            return None
    
    def build_custom_module(self, module_name: str, module_files: List[str],
                           main_file: str = None, include_stdlib: bool = False) -> Optional[Path]:
        """
        构建自定义模块
        
        Args:
            module_name: 模块名称
            module_files: 模块文件列表
            main_file: 主文件（可选）
            include_stdlib: 是否包含标准库
            
        Returns:
            Optional[Path]: 构建结果路径
        """
        logger.info(f"开始构建自定义模块: {module_name}")
        logger.info(f"模块文件: {module_files}")
        
        # 创建临时包
        temp_package = self.create_module_package(module_name, module_files, main_file)
        target_dir = temp_package / module_name
        
        # 构建参数
        build_params = [
            "--module",
            f"--output-dir={self.output_dir}",
            f"--jobs={os.cpu_count() or 4}",
            "--clang",
            "--lto=no",
            "--disable-ccache",
            "--python-flag=-O2",
            "--quiet"
        ]
        
        # 添加模块文件
        init_file = target_dir / "__init__.py"
        
        command = [sys.executable, "-m", "nuitka"] + build_params + [str(init_file)]
        
        try:
            logger.info(f"执行构建命令: {' '.join(command)}")
            result = subprocess.run(command, capture_output=True, text=True)
            
            if result.returncode == 0:
                logger.info("自定义模块构建成功")
                
                # 查找构建结果
                built_files = list(self.output_dir.glob(f"{module_name}.*"))
                if built_files:
                    result_file = built_files[0]
                    logger.info(f"构建结果: {result_file}")
                    return result_file
                else:
                    logger.error("未找到构建结果文件")
                    return None
            else:
                logger.error(f"构建失败，返回码: {result.returncode}")
                if result.stderr:
                    logger.error(f"错误信息: {result.stderr}")
                return None
                
        except Exception as e:
            logger.error(f"构建过程出错: {e}")
            return None
        finally:
            # 清理临时文件
            if temp_package.exists():
                shutil.rmtree(temp_package)
                logger.info(f"清理临时目录: {temp_package}")
    
    def build_multiple_modules(self, module_configs: Dict[str, Dict]) -> Dict[str, Optional[Path]]:
        """
        批量构建多个模块
        
        Args:
            module_configs: 模块配置字典
            
        Returns:
            Dict[str, Optional[Path]]: 构建结果
        """
        results = {}
        
        for module_name, config in module_configs.items():
            logger.info(f"构建模块: {module_name}")
            
            module_type = config.get('type', 'single')
            
            if module_type == 'single':
                # 单个文件模块
                source_path = config.get('source')
                result = self.build_module(module_name, source_path)
                
            elif module_type == 'custom':
                # 自定义模块
                module_files = config.get('files', [])
                main_file = config.get('main_file')
                result = self.build_custom_module(module_name, module_files, main_file)
                
            elif module_type == 'package':
                # 整个包
                source_path = config.get('source')
                result = self.build_module(module_name, source_path)
                
            else:
                logger.error(f"未知的模块类型: {module_type}")
                result = None
            
            results[module_name] = result
            
            if result:
                logger.info(f"模块 {module_name} 构建成功: {result}")
            else:
                logger.error(f"模块 {module_name} 构建失败")
        
        return results


class DynamicModuleLoader:
    """
    动态模块加载器
    支持从指定目录动态加载 PYD/SO 模块
    """
    
    def __init__(self, modules_dir: str = "./built_modules"):
        """
        初始化动态加载器
        
        Args:
            modules_dir: 模块目录
        """
        self.modules_dir = Path(modules_dir)
        self.loaded_modules = {}
        self.module_versions = {}
        
        # 确保模块目录存在
        self.modules_dir.mkdir(parents=True, exist_ok=True)
        
        # 添加模块目录到 Python 路径
        if str(self.modules_dir) not in sys.path:
            sys.path.insert(0, str(self.modules_dir))
        
        logger.info(f"初始化动态模块加载器，模块目录: {self.modules_dir}")
    
    def load_module(self, module_name: str, version: str = None) -> bool:
        """
        加载模块
        
        Args:
            module_name: 模块名称
            version: 版本信息（可选）
            
        Returns:
            bool: 是否加载成功
        """
        try:
            # 查找模块文件
            module_files = list(self.modules_dir.glob(f"{module_name}.*"))
            if not module_files:
                logger.error(f"未找到模块文件: {module_name}")
                return False
            
            # 选择最新的模块文件
            module_file = max(module_files, key=lambda x: x.stat().st_mtime)
            
            # 如果模块已加载且版本相同，跳过
            if module_name in self.loaded_modules:
                current_version = self.module_versions.get(module_name)
                if current_version == version:
                    logger.info(f"模块 {module_name} 已加载，版本相同，跳过")
                    return True
            
            # 卸载旧模块
            if module_name in self.loaded_modules:
                self.unload_module(module_name)
            
            # 动态导入模块
            spec = importlib.util.spec_from_file_location(module_name, module_file)
            if spec is None:
                logger.error(f"无法创建模块规范: {module_name}")
                return False
            
            module = importlib.util.module_from_spec(spec)
            sys.modules[module_name] = module
            spec.loader.exec_module(module)
            
            self.loaded_modules[module_name] = module
            self.module_versions[module_name] = version
            
            logger.info(f"模块加载成功: {module_name} (版本: {version})")
            return True
            
        except Exception as e:
            logger.error(f"加载模块失败 {module_name}: {e}")
            return False
    
    def unload_module(self, module_name: str) -> bool:
        """
        卸载模块
        
        Args:
            module_name: 模块名称
            
        Returns:
            bool: 是否卸载成功
        """
        try:
            if module_name in sys.modules:
                del sys.modules[module_name]
            
            if module_name in self.loaded_modules:
                del self.loaded_modules[module_name]
            
            if module_name in self.module_versions:
                del self.module_versions[module_name]
            
            logger.info(f"模块卸载成功: {module_name}")
            return True
            
        except Exception as e:
            logger.error(f"卸载模块失败 {module_name}: {e}")
            return False
    
    def reload_module(self, module_name: str) -> bool:
        """
        重新加载模块
        
        Args:
            module_name: 模块名称
            
        Returns:
            bool: 是否重新加载成功
        """
        version = self.module_versions.get(module_name)
        return self.load_module(module_name, version)
    
    def get_module(self, module_name: str):
        """
        获取已加载的模块
        
        Args:
            module_name: 模块名称
            
        Returns:
            模块对象或 None
        """
        return self.loaded_modules.get(module_name)
    
    def list_modules(self) -> List[str]:
        """
        列出所有可用模块
        
        Returns:
            List[str]: 模块名称列表
        """
        modules = []
        for file_path in self.modules_dir.glob("*"):
            if file_path.suffix in ['.pyd', '.so', '.dll']:
                modules.append(file_path.stem)
        
        return modules
    
    def check_module_update(self, module_name: str) -> bool:
        """
        检查模块是否有更新
        
        Args:
            module_name: 模块名称
            
        Returns:
            bool: 是否有更新
        """
        try:
            module_files = list(self.modules_dir.glob(f"{module_name}.*"))
            if not module_files:
                return False
            
            # 获取最新文件
            latest_file = max(module_files, key=lambda x: x.stat().st_mtime)
            
            # 如果模块未加载，认为有更新
            if module_name not in self.loaded_modules:
                return True
            
            # 比较文件修改时间
            # 这里可以添加更复杂的版本检查逻辑
            return True
            
        except Exception as e:
            logger.error(f"检查模块更新失败 {module_name}: {e}")
            return False


def create_module_loader_config(modules_dir: str = "./built_modules") -> Dict:
    """
    创建模块加载器配置文件
    
    Args:
        modules_dir: 模块目录
        
    Returns:
        Dict: 配置字典
    """
    config = {
        "modules_dir": modules_dir,
        "auto_load": True,
        "version_check": True,
        "modules": {}
    }
    
    return config


def save_module_config(config: Dict, config_path: str = "module_config.json"):
    """
    保存模块配置
    
    Args:
        config: 配置字典
        config_path: 配置文件路径
    """
    try:
        with open(config_path, 'w', encoding='utf-8') as f:
            json.dump(config, f, indent=2, ensure_ascii=False)
        logger.info(f"模块配置已保存: {config_path}")
    except Exception as e:
        logger.error(f"保存模块配置失败: {e}")


def load_module_config(config_path: str) -> Optional[Dict]:
    """
    加载模块配置
    
    Args:
        config_path: 配置文件路径
        
    Returns:
        Optional[Dict]: 配置字典或 None
    """
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f)
        logger.info(f"模块配置已加载: {config_path}")
        return config
    except Exception as e:
        logger.error(f"加载模块配置失败: {e}")
        return None


# ====================================================================
# 使用示例
# ====================================================================

if __name__ == "__main__":
    """
    模块打包和加载示例
    """
    
    print("=" * 60)
    print("Nuitka 模块打包工具示例")
    print("=" * 60)
    
    # 示例1: 构建单个模块
    print("\n示例1: 构建单个模块")
    print("-" * 40)
    
    builder = NuitkaModuleBuilder("./built_modules")
    
    # 假设有一个自定义模块文件
    # module_result = builder.build_module("my_module", "path/to/my_module.py")
    # if module_result:
    #     print(f"模块构建成功: {module_result}")
    
    # 示例2: 构建自定义模块
    print("\n示例2: 构建自定义模块")
    print("-" * 40)
    
    # 假设有多个模块文件
    # custom_files = ["module1.py", "module2.py", "utils.py"]
    # custom_result = builder.build_custom_module("custom_package", custom_files, "main.py")
    # if custom_result:
    #     print(f"自定义模块构建成功: {custom_result}")
    
    # 示例3: 批量构建模块
    print("\n示例3: 批量构建模块")
    print("-" * 40)
    
    module_configs = {
        "utils": {
            "type": "single",
            "source": "utils.py"
        },
        "data_processor": {
            "type": "custom",
            "files": ["processor.py", "validators.py"],
            "main_file": "processor.py"
        },
        "gui_components": {
            "type": "package",
            "source": "./gui"
        }
    }
    
    print("模块配置示例:")
    for name, config in module_configs.items():
        print(f"  {name}: {config}")
    
    # results = builder.build_multiple_modules(module_configs)
    
    # 示例4: 动态加载模块
    print("\n示例4: 动态加载模块")
    print("-" * 40)
    
    loader = DynamicModuleLoader("./built_modules")
    
    # 列出可用模块
    available_modules = loader.list_modules()
    print(f"可用模块: {available_modules}")
    
    # 加载模块示例
    # success = loader.load_module("my_module", "1.0.0")
    # if success:
    #     module = loader.get_module("my_module")
    #     print(f"模块加载成功: {module}")
    
    # 示例5: 模块配置管理
    print("\n示例5: 模块配置管理")
    print("-" * 40)
    
    config = create_module_loader_config("./built_modules")
    config["modules"] = {
        "my_module": {
            "version": "1.0.0",
            "auto_load": True,
            "dependencies": []
        },
        "utils": {
            "version": "2.0.0", 
            "auto_load": False,
            "dependencies": ["my_module"]
        }
    }
    
    save_module_config(config, "module_config.json")
    loaded_config = load_module_config("module_config.json")
    print(f"配置加载结果: {loaded_config}")
    
    print("\n" + "=" * 60)
    print("示例完成！请根据实际需求修改代码")
    print("=" * 60)